/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "resize_load_sb.h"

#include <locale.h>
#include <stdbool.h>
#include <time.h>

#include "node.h"
#include "device_manager.h"
#include "hmfs_common.h"

namespace OHOS {
namespace Hmfs {
ResizeLoadSb::ResizeLoadSb(ResizeLoad *loadPtr) : load_(loadPtr)
{
	config_ = load_->GetConfig();
	sbInfo_ = load_->GetSbInfo();
}

int32_t ResizeLoadSb::SetFeature(int32_t feature)
{
    if (config_->feature & NATIVE_TO_LE32(feature)) {
        if (!(sbInfo_->rawSuper->features & NATIVE_TO_LE32(feature))) {
            sbInfo_->rawSuper->features |= NATIVE_TO_LE32(feature);
            return EXIT_ERR_CODE;
        }
    }
    return SUCCESS_CODE;
}

void ResizeLoadSb::CheckSetFeatures(enum SB_ADDR sb_addr)
{
    bool requiresUpdate = false;
    if (SetFeature(HMFS_FEATURE_EXTRA_ATTR)) {
        HMFS_INFO("Fix set feature: extra_attr");
        requiresUpdate = true;
    }

    if (SetFeature(HMFS_FEATURE_PRJQUOTA)) {
        HMFS_INFO("Fix set feature: project_quota");
        requiresUpdate = true;
    }

    if (SetFeature(HMFS_FEATURE_CASEFOLD)) {
        struct SuperBlockData *superBlock = sbInfo_->rawSuper;
        SetLeValue(superBlock->encoding, config_->fileEncoding);
        SetLeValue(superBlock->encodingFlags, config_->fileEncodingFlags);
        HMFS_INFO("Fix set feature: casefold, encoding: %d, encodingFlags: %d",
            config_->fileEncoding, config_->fileEncodingFlags);
        requiresUpdate = true;
    }

    if (requiresUpdate) {
        load_->UpdateSuperBlock(sbInfo_->rawSuper, SB_MASK(sb_addr));
    }
}

int32_t ResizeLoadSb::CheckAddrBoundary(struct SuperBlockData *superBlock, enum SB_ADDR sbAddr)
{
    uint32_t segment0BlkId = GetLeValue(superBlock->segment0BlkId);
    uint32_t cpBlkId = GetLeValue(superBlock->cpBlkId);
    uint32_t sitBlkId = GetLeValue(superBlock->sitBlkId);
    uint32_t natBlkId = GetLeValue(superBlock->natBlkId);
    uint32_t ssaBlkId = GetLeValue(superBlock->ssaBlkId);;
    uint32_t mainBlkId = GetLeValue(superBlock->mainBlkId);
    uint32_t segmentCountInCP = GetLeValue(superBlock->segmentCountInCP);;
    uint32_t segmentCountInSIT = GetLeValue(superBlock->segmentCountInSIT);
    uint32_t segmentCountInNAT = GetLeValue(superBlock->segmentCountInNAT);
    uint32_t segmentCountInSSA = GetLeValue(superBlock->segmentCountInSSA);
    uint32_t segmentCountInMain = GetLeValue(superBlock->segmentCountInMain);
    uint32_t segmentCount = GetLeValue(superBlock->segmentCount);
    uint32_t logBlksPerSeg = GetLeValue(superBlock->logBlksPerSeg);
    uint64_t mainEndBlkAddr = mainBlkId + (segmentCountInMain << logBlksPerSeg);
    uint64_t segEndBlkAddr = segment0BlkId + (segmentCount << logBlksPerSeg);

    if (segment0BlkId != cpBlkId) {
        HMFS_ERROR("\tMismatch segment0(%u) cp_blkaddr(%u)", segment0BlkId, cpBlkId);
        return EXIT_ERR_CODE;
    }

    if (cpBlkId + (segmentCountInCP << logBlksPerSeg) != sitBlkId) {
        HMFS_ERROR("\tWrong CP boundary, start(%u) end(%u) blocks(%u)", cpBlkId, sitBlkId,
            segmentCountInCP << logBlksPerSeg);
        return EXIT_ERR_CODE;
    }

    if (sitBlkId + (segmentCountInSIT << logBlksPerSeg) != natBlkId) {
        HMFS_ERROR("\tWrong SIT boundary, start(%u) end(%u) blocks(%u)",
            sitBlkId, natBlkId, segmentCountInSIT << logBlksPerSeg);
        return EXIT_ERR_CODE;
    }

    if (natBlkId + (segmentCountInNAT << logBlksPerSeg) != ssaBlkId) {
        HMFS_ERROR("\tWrong NAT boundary, start(%u) end(%u) blocks(%u)",
            natBlkId, ssaBlkId, segmentCountInNAT << logBlksPerSeg);
        return EXIT_ERR_CODE;
    }

    if (ssaBlkId + (segmentCountInSSA << logBlksPerSeg) != mainBlkId) {
        HMFS_ERROR("\tWrong SSA boundary, start(%u) end(%u) blocks(%u)",
            ssaBlkId, mainBlkId, segmentCountInSSA << logBlksPerSeg);
        return EXIT_ERR_CODE;
    }

    if (mainEndBlkAddr > segEndBlkAddr) {
        HMFS_ERROR("\tWrong MAIN_AREA, start(%u) end(%u) block(%u)",
            mainBlkId, segment0BlkId + (segmentCount << logBlksPerSeg), segmentCountInMain << logBlksPerSeg);
        return EXIT_ERR_CODE;
    } else if (mainEndBlkAddr < segEndBlkAddr) {
        SetLeValue(superBlock->segmentCount, (mainEndBlkAddr - segment0BlkId) >> logBlksPerSeg);

        load_->UpdateSuperBlock(superBlock, SB_MASK(sbAddr));
        HMFS_ERROR("Fix alignment: start(%u) end(%u) block(%u)", mainBlkId,
            segment0BlkId + (segmentCount << logBlksPerSeg), segmentCountInMain << logBlksPerSeg);
    }
    return SUCCESS_CODE;
}

int32_t ResizeLoadSb::VerifySbChecksum(struct SuperBlockData *superBlock)
{
    if (SB_CHKSUM_OFFSET != GetLeValue(superBlock->checksumOffset)) {
        HMFS_ERROR("\tInvalid SB CRC offset: %u",
                    GetLeValue(superBlock->checksumOffset));
        return EXIT_ERR_CODE;
    }
    if (HmfsCommon::GetInstance().HmfsCrcValid(GetLeValue(superBlock->checksum), superBlock,
            GetLeValue(superBlock->checksumOffset))) {
        HMFS_ERROR("\tInvalid SB CRC: 0x%x", GetLeValue(superBlock->checksum));
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

int32_t ResizeLoadSb::CheckSuperBlock(struct SuperBlockData *superBlock, enum SB_ADDR sbAddr)
{
    if (superBlock == nullptr) {
        return EXIT_ERR_CODE;
    }

    if (HMFS_SUPER_MAGIC != GetLeValue(superBlock->magicNo)) {
        HMFS_ERROR("Magic Mismatch, valid(0x%x) - read(0x%x)",
            HMFS_SUPER_MAGIC, GetLeValue(superBlock->magicNo));
        return EXIT_ERR_CODE;
    }

    if ((GetLeValue(superBlock->features) & HMFS_FEATURE_SB_CHKSUM) && VerifySbChecksum(superBlock))
        return EXIT_ERR_CODE;

    uint32_t blockSize = 1 << GetLeValue(superBlock->logBlockSize);
    if (HMFS_BLKSIZE != blockSize) {
        HMFS_ERROR("Invalid blocksize (%u), supports only 4KB",
            blockSize);
        return EXIT_ERR_CODE;
    }

    /* check log blocks per segment */
    if (GetLeValue(superBlock->logBlksPerSeg) != 9) {
        HMFS_ERROR("Invalid log blocks per segment (%u)",
            GetLeValue(superBlock->logBlksPerSeg));
        return EXIT_ERR_CODE;
    }

    /* Currently, support 512/1024/2048/4096 bytes sector size */
    if (GetLeValue(superBlock->logSectorSize) > HMFS_MAX_LOG_SECTOR_SIZE ||
            GetLeValue(superBlock->logSectorSize)  < HMFS_MIN_LOG_SECTOR_SIZE) {
        HMFS_ERROR("Invalid log sectorsize (%u)", GetLeValue(superBlock->logSectorSize));
        return EXIT_ERR_CODE;
    }

    if (GetLeValue(superBlock->logSectorsPerBlk) + GetLeValue(superBlock->logSectorSize) !=
        HMFS_MAX_LOG_SECTOR_SIZE) {
        HMFS_ERROR("Invalid log sectors per block(%u) log sectorsize(%u)",
            GetLeValue(superBlock->logSectorsPerBlk), GetLeValue(superBlock->logSectorSize));
        return EXIT_ERR_CODE;
    }

    uint32_t segmentCount = GetLeValue(superBlock->segmentCount);
    uint32_t segsPerSec = GetLeValue(superBlock->segsPerSection);
    uint32_t secsPerZone = GetLeValue(superBlock->sectionsPerZone);
    uint32_t totalSections = GetLeValue(superBlock->sectionCount);
    uint32_t segsPerZone = segsPerSec * secsPerZone;

    /* blocks_per_seg should be 512, given the above check */
    uint32_t  blocksPerSeg = 1 << GetLeValue(superBlock->logBlksPerSeg);

    if (segmentCount > HMFS_MAX_SEGMENT || segmentCount < HMFS_MIN_SEGMENTS) {
        HMFS_ERROR("\tInvalid segment count (%u)", segmentCount);
        return EXIT_ERR_CODE;
    }

    if (!(GetLeValue(superBlock->features) & NATIVE_TO_LE32(HMFS_FEATURE_RO)) && (totalSections > segmentCount ||
        totalSections < HMFS_MIN_SEGMENTS || segsPerSec > segmentCount || !segsPerSec)) {
        HMFS_ERROR("\tInvalid segment/section count (%u, %u x %u)",
            segmentCount, totalSections, segsPerSec);
        return EXIT_ERR_CODE;
    }

    if ((segmentCount / segsPerSec) < totalSections) {
        HMFS_ERROR("Small segmentCount (%u < %u * %u)",
            segmentCount, segsPerSec, totalSections);
        return EXIT_ERR_CODE;
    }

    if (segmentCount > (GetLeValue(superBlock->blockCount) >> 9)) {
        HMFS_ERROR("Wrong segmentCount / block_count (%u > %" PRIu64 ")",
            segmentCount, GetLeValue(superBlock->blockCount));
        return EXIT_ERR_CODE;
    }

    if (superBlock->devices[0].path[0]) {  //TODO modify
        uint32_t dev_segs = LE32_TO_NATIVE(superBlock->devices[0].segmentCount);
        uint32_t i = 1;
        while (i < MAX_DEVICES && superBlock->devices[i].path[0]) {
            dev_segs += LE32_TO_NATIVE(superBlock->devices[i].segmentCount);
            i++;
        }
        if (segmentCount != dev_segs / segsPerZone * segsPerZone) {
            HMFS_ERROR("Segment count (%u) mismatch with total segments from devices (%u)",
                segmentCount, dev_segs);
            return EXIT_ERR_CODE;
        }
    }

    if (secsPerZone > totalSections || !secsPerZone) {
        HMFS_ERROR("Wrong secsPerZone / total_sections (%u, %u)", secsPerZone, totalSections);
        return EXIT_ERR_CODE;
    }
    if (GetLeValue(superBlock->coldExtensionCount) > HMFS_MAX_EXTENSION || superBlock->hotExtensionCount >
        HMFS_MAX_EXTENSION || GetLeValue(superBlock->coldExtensionCount) + superBlock->hotExtensionCount >
        HMFS_MAX_EXTENSION) {
        HMFS_ERROR("Corrupted extension count (%u + %u > %u)", GetLeValue(superBlock->coldExtensionCount),
            superBlock->hotExtensionCount, HMFS_MAX_EXTENSION);
        return EXIT_ERR_CODE;
    }

    if (GetLeValue(superBlock->cpPayload) > (blocksPerSeg - HMFS_CP_PACKS)) {
        HMFS_ERROR("Insane cp_payload (%u > %u)",
            GetLeValue(superBlock->cpPayload), blocksPerSeg - HMFS_CP_PACKS);
        return EXIT_ERR_CODE;
    }

    /* check reserved ino info */
    if (GetLeValue(superBlock->nodeInodeId) != 1 || GetLeValue(superBlock->metaInodeId) != 2 ||
        GetLeValue(superBlock->rootInodeId) != 3) {
        HMFS_ERROR("Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)",
            GetLeValue(superBlock->nodeInodeId), GetLeValue(superBlock->metaInodeId), GetLeValue(superBlock->rootInodeId));
        return EXIT_ERR_CODE;
    }

    /* Check zoned block device feature */
    if (config_->devices[0].zonedModel != HMFS_ZONED_NONE && !(superBlock->features &
        NATIVE_TO_LE32(HMFS_FEATURE_BLKZONED))) {
        HMFS_ERROR("\tMissing zoned block device feature");
        return EXIT_ERR_CODE;
    }

    if (CheckAddrBoundary(superBlock, sbAddr)) {
        return EXIT_ERR_CODE;
    }
    return SUCCESS_CODE;
}

bool ResizeLoadSb::isCheckpointStop(struct SuperBlockData *superBlock, bool abnormal)
{
    for (int32_t i = 0; i < STOP_CP_REASON_MAX; i++) {
        if (abnormal && i == STOP_CP_REASON_SHUTDOWN) {
            continue;
        }
        if (superBlock->stopReason[i]) {
            return true;
        }
    }
    return false;
}

bool ResizeLoadSb::isInconsistentError(struct SuperBlockData *superBlock)
{
    if (superBlock == nullptr) {
        return false;
    }
    for (int32_t i = 0; i < MAX_HMFS_ERRORS; i++) {
        if (superBlock->errors[i]) {
            return true;
        }
    }
    return false;
}

int32_t ResizeLoadSb::ValidateSuperBlock(enum SB_ADDR sbAddr)
{
    HMFS_DEBUG("Enter ValidateSuperBlock");
    char buf[HMFS_BLKSIZE];
    sbInfo_->rawSuper = static_cast<struct SuperBlockData *>(malloc(sizeof(struct SuperBlockData)));
    if (!sbInfo_->rawSuper) {
        return EXIT_ERR_CODE;
    }
    if (HmfsIo::GetInstance().DevReadBlock(buf, sbAddr)) {
        HMFS_ERROR("failed to read superblock!");
        return EXIT_ERR_CODE;
    }
    memcpy(sbInfo_->rawSuper, buf + HMFS_SUPER_OFFSET, sizeof(struct SuperBlockData));
    if (!CheckSuperBlock(sbInfo_->rawSuper, sbAddr)) {
        CheckSetFeatures(sbAddr);
        /* get kernel version */
        if (config_->kd >= 0) {
            HmfsIo::GetInstance().DevReadVersion(config_->version, 0, VERSION_NAME_LEN);
            HmfsCommon::GetInstance().GetKernelVersion(config_->version);
        } else {
            HmfsCommon::GetInstance().GetKernelUnameVersion(config_->version);
        }
        /* build sb version */
        memcpy(config_->sbVersion, sbInfo_->rawSuper->version, VERSION_NAME_LEN);
        HmfsCommon::GetInstance().GetKernelVersion(config_->sbVersion);
        memcpy(config_->initVersion, sbInfo_->rawSuper->initVersion, VERSION_NAME_LEN);
        HmfsCommon::GetInstance().GetKernelVersion(config_->initVersion);
        config_->forceStop = isCheckpointStop(sbInfo_->rawSuper, false);
        config_->abnormalStop = isCheckpointStop(sbInfo_->rawSuper, true);
        config_->hmfsErrors = isInconsistentError(sbInfo_->rawSuper);

        HMFS_INFO("RESIZE version\n  \"%s\"", config_->initVersion);
        HMFS_INFO("FSCK version\n  from \"%s\"\n    to \"%s\"", config_->sbVersion, config_->version);
        PrintSbDataFeature(sbInfo_->rawSuper);
        PrintSbDataStopReason(sbInfo_->rawSuper, config_->forceStop);
        PrintSbDataErrors(sbInfo_->rawSuper, config_->hmfsErrors);
        return SUCCESS_CODE;
    }

    free(sbInfo_->rawSuper);
    sbInfo_->rawSuper = nullptr;
    HMFS_ERROR("\tCan't find a valid F2FS superblock at 0x%x", sbAddr);
    return -EINVAL;
}

int32_t ResizeLoadSb::CheckSectorSize(struct SuperBlockData *superBlock)
{
    if (superBlock == nullptr) {
        return EXIT_ERR_CODE;
    }
    uint32_t logSectorSize = HmfsCommon::GetInstance().LogBase2(config_->sectorSize);
    uint32_t logSectorsPerBlock = HmfsCommon::GetInstance().LogBase2(config_->sectorsPerBlock);
    //TODO 相等了？待调试
    if (logSectorSize == GetLeValue(superBlock->logSectorSize) &&
        logSectorsPerBlock == GetLeValue(superBlock->logSectorsPerBlk)) {
        return SUCCESS_CODE;
    }
    HMFS_INFO("RESIZE version\n  \"%s\"", config_->initVersion);
    SetLeValue(superBlock->logSectorSize, logSectorSize);
    SetLeValue(superBlock->logSectorsPerBlk, logSectorsPerBlock);

    load_->UpdateSuperBlock(superBlock, SB_MASK_ALL);
    return SUCCESS_CODE;
}

int32_t ResizeLoadSb::HmfsLoadSbData()
{
    HMFS_DEBUG("Enter HmfsLoadSbData");
    if (sbInfo_ == nullptr) {
        return EXIT_ERR_CODE;
    }
    int32_t ret = ValidateSuperBlock(SB0_ADDR);
    if (ret) {
        ret = ValidateSuperBlock(SB1_ADDR);
        if (ret) {
            //dump_sbi_info(sbInfo_);  //TODO modify
            return EXIT_ERR_CODE;
        }
    }
    SuperBlockData *sbData = sbInfo_->rawSuper;
    ret = CheckSectorSize(sbData);
    if (ret) {
        return EXIT_ERR_CODE;
    }
    PrintRawSbInfo(sbData, config_->debugLevel, config_->layout); 
    return SUCCESS_CODE;
}

} // namespace Hmfs
} // namespace OHOS
